// Package yotsuba defines internal datatypes for board content. Along 
// with 3-way logic sql types used by the web UI, there are simpler
// structs with basic datatypes. These are defined here separately
// to avoid ugly sql.NullWhatever from polluting the code for the crawler
// which has no use for it. Some changes to either set of structures may
// require corresponding updates for the other.
package yotsuba

import (
	"database/sql"
	"fmt"
	"html/template"
	"regexp"
	"strings"
	"time"
)

const YotsubaTime = "01/02/06(Mon)15:04:05"
const NihTime = "2006-01-02 (Mon) 15:04:05"

type Authority int

const (
	User Authority = iota
	Mod
	Admin
)

var AuthType = []string{"user", "mod", "admin"}

type SqlImage struct {
	Md5       sql.NullString
	Size      sql.NullInt64
	FullX     sql.NullInt64
	FullY     sql.NullInt64
	ThumbX    sql.NullInt64
	ThumbY    sql.NullInt64
	ThumbFile sql.NullString
	ImgFile   sql.NullString
}

type SqlPost struct {
	IntId       sql.NullInt64
	PostId      sql.NullInt64
	GhostId     sql.NullInt64
	Time        *time.Time
	Name        sql.NullString
	Trip        sql.NullString
	Subject     sql.NullString
	Mail        sql.NullString
	PosterUid   sql.NullString
	Text        sql.NullString
	EscapedBody template.HTML
	Spoiler     sql.NullBool
	PostDeleted *time.Time
	Parent      sql.NullInt64
	ImgPath     sql.NullString
	Ip          sql.NullString
	Pass        sql.NullString
	ImgIntId    sql.NullInt64 // DB foreign key reference for looking up image.
	Img         *SqlImage
	ImgName     sql.NullString
	ImgUrl      sql.NullString
	ThumbUrl    sql.NullString
	Highlight   sql.NullBool
	Auth        sql.NullString
	FileDeleted sql.NullBool
	FmtPostNo   string
	FmtTime     string
	FmtClass    string
	FmtPostDel  string
	FmtImgUrl   string
}

type SqlThread struct {
	OpIntId      sql.NullInt64 // DB foreign key reference for looking up first post.
	Sticky       sql.NullBool
	Locked       sql.NullBool
	Deleted      sql.NullBool
	Expired      *time.Time
	FullThread   bool // cue template to present slightly differently
	PostCount    sql.NullInt64
	ImgCount     sql.NullInt64
	PostsOmitted sql.NullInt64
	ImgsOmitted  sql.NullInt64
	Head         *SqlPost   // OP
	Tail         []*SqlPost // Trailing posts on board top page.
	ThreadUrl    sql.NullString
}

type Image struct {
	Md5    string
	Size   int
	FullX  int
	FullY  int
	ThumbX int
	ThumbY int
}

type Post struct {
	Id          int
	Name        string
	Trip        string
	Mail        string
	Subject     string
	PosterUid   string
	Spoiler     bool
	Text        string
	Time        time.Time
	Img         *Image
	ImgName     string
	ImgUrl      string
	ThumbUrl    string
	Highlight   bool
	Auth        Authority
	FileDeleted bool
	RedText     bool
}

type Thread struct {
	Id     int
	Url    string
	Page   int
	Locked bool
	Sticky bool
	Posts  []*Post
}

func (thread *Thread) String() string {
	return fmt.Sprintf("Thread #%d (%s), page %d, Locked: %t, Sticky %t",
		thread.Id, thread.Url, thread.Page, thread.Locked, thread.Sticky)
}

func (post *Post) String() string {
	postFormat := `
%s%s <%s> UID: <%s> %s No.%d
Hi : %t / Auth: %v
%s`
	imgFormat := `
%s%s <%s> UID: <%s> %s No.%d
Hi : %t / Auth: %v
File: %d, %s, %dx%d
Image: %s Del: %t
Thumb: %s - %dx%d
MD5: %s Spoiler: %t
%s`

	var str string
	if post.Img == nil {
		str = fmt.Sprintf(postFormat,
			post.Name, post.Trip, post.Mail, post.PosterUid, post.Time.Format(NihTime), post.Id,
			post.Highlight, post.Auth,
			post.Text)

	} else {
		str = fmt.Sprintf(imgFormat,
			post.Name, post.Trip, post.Mail, post.PosterUid, post.Time.Format(NihTime), post.Id,
			post.Highlight, post.Auth,
			post.Img.Size, post.ImgName, post.Img.FullX, post.Img.FullY,
			post.ImgUrl, post.FileDeleted,
			post.ThumbUrl, post.Img.ThumbX, post.Img.ThumbY,
			post.Img.Md5, post.Spoiler,
			post.Text)
	}
	return str
}

var EscapeBBCode *strings.Replacer
var UnescapeBBCode *strings.Replacer
var BBtoHTML func(string, string, int64) string

func init() {
	EscapeBBCode = strings.NewReplacer(
		"#", "#p;",
		"[color", "#lb;color", "[/color", "#lb;/color",
		"[hi", "#lb;/hi", "[/hi", "#lb;/hi")

	UnescapeBBCode = strings.NewReplacer(
		"[hi]", `<span class="hi">`, "[/hi]", `</span>`,
		"[color=red]", `<span class="red">`, "[/color]", `</span>`,
		"[color=green]", `<span class="red">`, "[/color]", `</span>`,
		"[spoiler]", `<span class="spoiler">`, "[/spoiler]", `</span>`,
		"[line_quote]", `<span class="line_quote">`,
		"[/line_quote]", `</span>`,
		"#lb;", "[",
		"#p;", "#")

	lineQuote := regexp.MustCompile(`^&gt;[^&].*?$`)
	postQuote := regexp.MustCompile(`&gt;&gt;(\d+(?:,\d+)?)`)
	//boardQuote := regexp.MustCompile(`&gt;&gt;&gt;/(\S)/(\d+?)`)

	BBtoHTML = func(a string, bname string, parent int64) string {
		lines := strings.Split(a, "\n")
		for i, line := range lines {
			line = UnescapeBBCode.Replace(template.HTMLEscapeString(line))
			line = lineQuote.ReplaceAllString(
				line, `<span class="line_quote">$0</span>`)
			line = postQuote.ReplaceAllString(
				line, fmt.Sprintf(
					`<a href="/%s/res/%d#a_$1" class="post_quote">$0</a>`,
					bname, parent))
			lines[i] = line
		}
		return strings.Join(lines, "<br/>")
	}
}
